7.04. Как работает CI/CD
1. Как работает CI/CD
1.1. Как работает CI
Непрерывная интеграция — это практика разработки программного обеспечения, при которой изменения, вносимые разработчиками в общий репозиторий исходного кода, автоматически и регулярно объединяются (интегрируются) в основную ветвь кодовой базы. Интеграция сопровождается автоматической сборкой системы и выполнением набора тестов, предназначенных для проверки корректности внесённых изменений и сохранения работоспособности всей системы.
Технически процесс CI запускается триггером — чаще всего это push в определённую ветку репозитория (например, в main, develop или feature-ветку). Система CI (например, Jenkins, GitLab CI, GitHub Actions, Azure DevOps) перехватывает этот триггер, клонирует репозиторий, устанавливает необходимые зависимости, компилирует код (если требуется), и последовательно выполняет:
- Сборку (build): преобразование исходного кода в исполняемый артефакт;
- Статический анализ кода: проверка соответствия кода стандартам, поиск потенциальных уязвимостей или антишаблонов;
- Запуск автоматизированных тестов: unit-, интеграционные, иногда end-to-end тесты;
- Формирование отчётов: о покрытии кода тестами, времени выполнения, количестве найденных ошибок.
Если все этапы завершены успешно, интеграция считается пройденной. В противном случае — система CI сигнализирует о сбое и часто блокирует дальнейшее продвижение изменений (например, не позволяет смёрджить pull request).
1.2. Как организовать CI
Организация CI требует как инфраструктурных, так и процессных решений:
- Централизованный репозиторий исходного кода с чёткой политикой ветвления;
- Система CI, способная выполнять сборку и тестирование в изолированной среде;
- Единая система зависимостей и воспроизводимая сборка (с помощью lock-файлов, Dockerfile, Makefile и т.п.);
- Минимальный набор автоматизированных тестов, покрывающих критические участки логики;
- Культура частых и небольших коммитов — чтобы уменьшить риск конфликтов и упростить отладку неудачных сборок.
Важно понимать, что CI — это не просто «скрипт, который запускается после коммита». Это дисциплина, в рамках которой каждый коммит рассматривается как кандидат на доставку в production, и, следовательно, должен проходить строгую проверку.
2. Непрерывная доставка и непрерывное развёртывание (Continuous Delivery / Continuous Deployment)
2.1. Как работает CD
Непрерывная доставка (Continuous Delivery) — это расширение практики CI, при котором система проверяет изменения и подготавливает их к развёртыванию в рабочую среду. Иными словами, после прохождения CI каждый успешный билд становится готовым к развёртыванию в production — в любой момент и с минимальными усилиями.
Непрерывное развёртывание (Continuous Deployment) — это дальнейшее развитие идеи: каждое изменение, успешно прошедшее CI и все необходимые проверки, автоматически развёртывается в production, без вмешательства человека.
Таким образом, отличие между Delivery и Deployment заключается в наличии или отсутствии ручного подтверждения перед попаданием в production. Continuous Delivery предполагает, что развёртывание может быть выполнено вручную по решению команды; Continuous Deployment делает это решение автоматическим.
Процесс CD включает:
- Создание артефакта (например, Docker-образа, JAR-файла, ZIP-архива), идентифицируемого по хешу коммита;
- Тестирование на staging-среде (предпродакшене), максимально приближённой к production;
- Управление конфигурациями и переменными окружения отдельно от кода;
- Плавное развёртывание (blue-green, canary, rolling update) для минимизации простоя и рисков;
- Механизмы мониторинга и автоматического отката при обнаружении сбоев.
2.2. Как организовать CD
Для организации CD требуется более зрелая инфраструктура и культура, чем для CI:
- Единый артефакт, проходящий все стадии без пересборки;
- Идемпотентные скрипты развёртывания, не зависящие от состояния целевой системы;
- Среды, идентичные друг другу по конфигурации (с возможным различием только в данных);
- Автоматизированные проверки качества развёртывания: smoke-тесты, health-check, метрики доступности;
- Политики безопасности: управление секретами, ограничение прав на развёртывание, аудит изменений.
Ключевое правило CD: если что-то нельзя автоматизировать — это узкое место, которое необходимо устранить. CD требует доверия к автоматике, а доверие строится на стабильности и прозрачности.
3. Среды разработки и эксплуатации
Эффективная реализация CI/CD невозможна без чёткого разграничения и управления средами — изолированными экземплярами системы, предназначенными для разных целей в жизненном цикле разработки.
3.1. Локальная разработка
Каждый разработчик работает на собственной машине, используя локально установленные инструменты или контейнеры. Цель — написать и протестировать небольшой функционал в условиях, приближённых к реальным. Однако локальная среда по определению неполна: она не включает зависимости, характерные для распределённой системы (например, очереди сообщений, внешние API, балансировщики).
3.2. Тестовая среда (test/staging)
Тестовая среда — это первый интеграционный уровень, где проверяется взаимодействие компонентов системы. Она может быть частично или полностью клонированной из production и используется для:
- Запуска интеграционных и end-to-end тестов;
- Проверки совместимости между сервисами;
- Валидации миграций баз данных.
Важно, чтобы тестовая среда была контролируемой: данные в ней не должны быть критичными, но должны отражать типичные сценарии использования.
3.3. Предпродакшен (pre-production)
Предпродакшен — это точное зеркало production, включая конфигурации, версии зависимостей, топологию сети и объёмы данных (или их анонимизированные копии). Его цель — финальная валидация перед выходом в продакшен. Здесь запускаются:
- Performance-тесты;
- Security-сканирования;
- Business-валидация (QA, заказчик, аналитики).
Если система работает корректно в предпроде, она считается готовой к развёртыванию в production.
3.4. Продакшен (production)
Продакшен — это реальная эксплуатационная среда, в которой система обслуживает конечных пользователей. Здесь важнейшими критериями становятся:
- Доступность (availability);
- Надёжность (reliability);
- Восстанавливаемость (recoverability).
Любое вмешательство в продакшен должно быть минимальным, контролируемым и обратимым.
4. Варианты развёртывания: от физических серверов до контейнеров
Развёртывание программного обеспечения — это процесс установки, конфигурации и активации системы в целевой среде. Выбор архитектуры развёртывания напрямую влияет на сложность реализации CI/CD, скорость доставки и надёжность эксплуатации.
4.1. Физические серверы с прямым доступом
Исторически первым способом размещения ПО были выделенные физические серверы, на которых вручную устанавливались операционная система, зависимости и само приложение. Управление осуществлялось через SSH или консольные сессии.
Преимущества:
- Полный контроль над «железом»;
- Минимальные накладные расходы на виртуализацию.
Недостатки:
- Высокая сложность масштабирования;
- Слабая воспроизводимость сред («на моей машине работает»);
- Длительное время развёртывания;
- Сложность автоматизации без использования систем конфигурации (Ansible, Puppet, Chef).
В контексте CI/CD физические серверы требуют тщательной автоматизации доставки кода и настройки среды. Без этого практики непрерывной доставки становятся нестабильными и подвержены человеческим ошибкам.
4.2. Виртуальные машины (ВМ)
Виртуализация позволила изолировать приложения на уровне операционной системы, создавая воспроизводимые образы с предустановленными зависимостями. Каждое приложение может работать в собственной виртуальной машине, что упрощает управление версиями и изоляцию.
Преимущества:
- Полная изоляция окружения;
- Возможность клонирования и быстрого восстановления;
- Поддержка мультиплатформенности (Linux/Windows на одном хосте).
Недостатки:
- Значительные накладные расходы на память и CPU;
- Длительное время запуска по сравнению с контейнерами;
- Сложность оркестрации при большом числе ВМ.
В CI/CD-конвейерах виртуальные машины часто используются как тестовые или staging-среды, однако в production-развёртываниях их постепенно вытесняют более лёгкие и гибкие решения.
4.3. Контейнеризация
Контейнеризация — это парадигма, при которой приложение и все его зависимости упаковываются в изолированный, переносимый образ, выполняемый в общей ОС хоста с использованием механизмов ядра (cgroups, namespaces). Наиболее распространённая реализация — Docker.
Контейнеры стали де-факто стандартом в CI/CD благодаря следующим свойствам:
- Иммутабельность: образ создаётся один раз и используется на всех стадиях;
- Идемпотентность: запуск одного и того же образа всегда даёт одинаковый результат;
- Лёгкость: контейнеры запускаются за секунды и потребляют минимальные ресурсы;
- Переносимость: один и тот же образ может работать локально, в облаке, на bare metal.
В сочетании с системами оркестрации (Kubernetes, Docker Swarm) контейнеризация позволяет реализовать масштабируемые, самовосстанавливающиеся и легко управляемые платформы. Именно с контейнерами наиболее органично сочетаются практики CI/CD:
- После прохождения CI создаётся Docker-образ с уникальным тегом (например, по хешу коммита);
- Образ помещается в registry (Docker Hub, Harbor, ECR и т.п.);
- На staging или production система оркестрации подтягивает новый образ и запускает его в соответствии с политикой развёртывания.
Такой подход обеспечивает единый артефакт, проходящий все этапы без пересборки, что исключает так называемый «эффект дивана» («работает в тесте — не работает в проде»).
5. Откат и вывод из эксплуатации
Несмотря на все меры предосторожности, не все изменения оказываются стабильными в production. Поэтому CI/CD-конвейеры должны включать механизмы отката и процедуры вывода из эксплуатации.
5.1. Откат (rollback)
Откат — это процесс возвращения системы к предыдущему известному рабочему состоянию. Он может быть:
- Автоматическим: при срабатывании триггеров мониторинга (например, рост ошибок 5xx, падение метрики liveness);
- Ручным: по решению инженера или дежурного.
Для эффективного отката необходимы:
- Хранение предыдущих версий артефактов (образов, пакетов);
- Идемпотентные и обратимые миграции баз данных (или стратегия «только вперёд» с двойной совместимостью);
- Версионирование конфигураций (через GitOps, например);
- Минимальное время восстановления (MTTR).
Важно: откат не всегда возможен. Например, при изменении структуры данных без поддержки обратной совместимости. Поэтому в зрелых системах часто применяются стратегии развёртывания без отката, такие как feature toggles и постепенное включение функционала.
5.2. Вывод из эксплуатации (decommissioning)
Вывод из эксплуатации — это процесс полного или частичного прекращения работы компонента системы. Это может быть связано с:
- Заменой сервиса на новый;
- Устареванием функционала;
- Снижением нагрузки до нуля.
Процедура включает:
- Уведомление зависимых систем;
- Миграция или архивирование данных;
- Удаление маршрутов и DNS-записей;
- Остановку и удаление контейнеров/виртуальных машин;
- Документирование и аудит.
В CI/CD-контексте важно, чтобы процесс вывода тоже был автоматизирован и проверяем. Например, в GitOps-подходе удаление манифеста из репозитория приводит к автоматическому уничтожению ресурса в кластере.